﻿@page "/traces/detail/{traceId}"

@using Aspire.Dashboard.Model.Otlp
@using Aspire.Dashboard.Otlp.Model
@using System.Globalization
@using Aspire.Dashboard.Resources
@using Aspire.Dashboard.Utils
@inject IStringLocalizer<Dashboard.Resources.TraceDetail> Loc
@inject IStringLocalizer<ControlsStrings> ControlStringsLoc

<PageTitle><ApplicationName ResourceName="@nameof(Dashboard.Resources.TraceDetail.TraceDetailPageTitle)" Loc="@Loc" /></PageTitle>

@if (_trace is { } trace)
{
    <div class="trace-detail-layout">
        <div class="trace-detail-header page-header">
            <h1 style="display:inline-block">@GetResourceName(trace.FirstSpan.Source): @trace.FirstSpan.Name</h1>
            <span class="trace-id">@OtlpHelpers.ToShortenedId(trace.TraceId)</span>
        </div>
        <FluentToolbar Orientation="Orientation.Horizontal">
            <div>
                @Loc[nameof(Dashboard.Resources.TraceDetail.TraceDetailTraceStartHeader)] <strong title="@FormatHelpers.FormatDateTime(TimeProvider, _trace.FirstSpan.StartTime, MillisecondsDisplay.Full)">@FormatHelpers.FormatDateTime(TimeProvider, _trace.FirstSpan.StartTime, MillisecondsDisplay.Truncated)</strong>
            </div>
            <FluentDivider Role="DividerRole.Presentation" Orientation="Orientation.Vertical" />
            <div>
                @Loc[nameof(Dashboard.Resources.TraceDetail.TraceDetailDurationHeader)] <strong>@DurationFormatter.FormatDuration(trace.Duration)</strong>
            </div>
            <FluentDivider Role="DividerRole.Presentation" Orientation="Orientation.Vertical" />
            <div>
                @Loc[nameof(Dashboard.Resources.TraceDetail.TraceDetailResourcesHeader)] <strong>@trace.Spans.GroupBy(s => s.Source).Count()</strong>
            </div>
            <FluentDivider Role="DividerRole.Presentation" Orientation="Orientation.Vertical" />
            <div>
                @Loc[nameof(Dashboard.Resources.TraceDetail.TraceDetailDepthHeader)] <strong>@_maxDepth</strong>
            </div>
            <FluentDivider Role="DividerRole.Presentation" Orientation="Orientation.Vertical" />
            <div>
                @Loc[nameof(Dashboard.Resources.TraceDetail.TraceDetailTotalSpansHeader)] <strong>@trace.Spans.Count</strong>
            </div>
            <FluentAnchor slot="end" Appearance="Appearance.Lightweight" Href="@DashboardUrls.StructuredLogsUrl(traceId: trace.TraceId)">@ControlStringsLoc[nameof(ControlsStrings.ViewLogsLink)]</FluentAnchor>
        </FluentToolbar>

        <SummaryDetailsView
            ShowDetails="SelectedSpan is not null"
            OnDismiss="@(() => ClearSelectedSpanAsync(causedByUserAction: true))"
            ViewKey="TraceDetail"
            SelectedValue="@SelectedSpan">
            <DetailsTitleTemplate>
                @{ var shortedSpanId = OtlpHelpers.ToShortenedId(SelectedSpan!.Span.SpanId); }
                <div class="pane-details-title" title="@($"{SelectedSpan!.Title} ({shortedSpanId})")">
                    @SelectedSpan!.Title
                    <span class="pane-details-subtext">@shortedSpanId</span>
                </div>
            </DetailsTitleTemplate>
            <Summary>
                <FluentDataGrid Virtualize="true" GenerateHeader="GenerateHeaderOption.Sticky" Class="trace-view-grid" ResizableColumns="true" ItemsProvider="@GetData" TGridItem="SpanWaterfallViewModel" RowClass="@GetRowClass" GridTemplateColumns="4fr 12fr 85px">
                    <TemplateColumn Title="Name">
                        <div class="col-long-content" title="@context.GetTooltip()" @onclick="() => OnShowPropertiesAsync(context, null)">
                            @{
                                var isServerOrConsumer = context.Span.Kind == OtlpSpanKind.Server || context.Span.Kind == OtlpSpanKind.Consumer;
                                // Indent the span name based on the depth of the span.
                                var marginLeft = (context.Depth - 1) * 15;

                                // We want to have consistent margin for both client and server spans.
                                string spanNameContainerStyle;
                                if (!isServerOrConsumer)
                                {
                                    // Client span has 19px extra content:
                                    // - 5px extra margin-left
                                    // - 5px border
                                    // - 9px padding-left
                                    spanNameContainerStyle = $"margin-left: 5px; border-left-color: {ColorGenerator.Instance.GetColorHexByKey(GetResourceName(context.Span.Source))}; border-left-width: 5px; border-left-style: solid; padding-left: 9px;";
                                }
                                else
                                {
                                    // Span with icon has 19px extra content:
                                    // - 16px icon
                                    // - 3px padding-left
                                    spanNameContainerStyle = string.Empty;
                                }
                            }

                            <span style="margin-left: @(marginLeft)px;">
                                <span class="span-collapse-symbol" @onclick="() => OnToggleCollapse(context)" @onclick:stopPropagation="true">
                                    @if (context.Children.Count > 0)
                                    {
                                        @(context.IsCollapsed ? '+' : '-')
                                    }
                                </span>
                                <span class="span-name-container" style="@spanNameContainerStyle">
                                    @if (isServerOrConsumer)
                                    {
                                        <FluentIcon Class="span-kind-icon"
                                                    Color="Color.Custom"
                                                    CustomColor="@ColorGenerator.Instance.GetColorHexByKey(GetResourceName(context.Span.Source))"
                                                    Value="@GetSpanIcon(context.Span)" />
                                    }

                                    @if (context.IsError)
                                    {
                                        <FluentIcon Icon="Icons.Filled.Size12.ErrorCircle" Color="Color.Error" Class="trace-tag-icon" />
                                    }
                                    @GetResourceName(context.Span.Source)
                                    @if (context.HasUninstrumentedPeer)
                                    {
                                        <span class="uninstrumented-peer">
                                            @{
                                                Icon icon;
                                                if (context.Span.Attributes.HasKey("db.system"))
                                                {
                                                    icon = new Icons.Filled.Size16.Database();
                                                }
                                                else if (context.Span.Attributes.HasKey("messaging.system"))
                                                {
                                                    icon = new Icons.Filled.Size16.Mail();
                                                }
                                                else
                                                {
                                                    // Everything else.
                                                    icon = new Icons.Filled.Size16.ArrowCircleRight();
                                                }
                                            }
                                            <FluentIcon
                                                Style="@($"fill: {ColorGenerator.Instance.GetColorHexByKey(context.UninstrumentedPeer)};")"
                                                Value="icon"
                                                Class="uninstrumented-peer-icon" />
                                            @context.UninstrumentedPeer
                                        </span>
                                    }
                                    <span class="span-row-name">@context.GetDisplaySummary()</span>
                                </span>
                            </span>
                        </div>
                    </TemplateColumn>
                    <TemplateColumn>
                        <HeaderCellItemTemplate>
                            <div class="ticks">
                                <div class="tick" style="grid-column: 1;"></div>
                                <span class="tick-label" style="grid-column: 1;">@DurationFormatter.FormatDuration(TimeSpan.Zero)</span>

                                <div class="tick" style="grid-column: 2;"></div>
                                <span class="tick-label" style="grid-column: 2;">@DurationFormatter.FormatDuration(trace.Duration / 4)</span>

                                <div class="tick" style="grid-column: 3;"></div>
                                <span class="tick-label" style="grid-column: 3;">@DurationFormatter.FormatDuration(trace.Duration / 4 * 2)</span>

                                <div class="tick" style="grid-column: 4;"></div>
                                <span class="tick-label" style="grid-column: 4;">@DurationFormatter.FormatDuration(trace.Duration / 4 * 3)</span>

                                <span class="tick-label end-tick" style="grid-column: 4;">@DurationFormatter.FormatDuration(trace.Duration)</span>
                                <div class="tick" style="grid-column: 5;"></div>
                            </div>
                        </HeaderCellItemTemplate>
                        <ChildContent>
                            <div class="ticks" @onclick="() => OnShowPropertiesAsync(context, null)">
                                <div class="span-container" style="grid-template-columns: @context.LeftOffset.ToString("F2", CultureInfo.InvariantCulture)% @context.Width.ToString("F2", CultureInfo.InvariantCulture)% min-content;">
                                    <div class="span-bar" style="grid-column: 2; background: @ColorGenerator.Instance.GetColorHexByKey(GetResourceName(context.Span.Source));"></div>
                                    <div class="span-bar-label @(context.LabelIsRight ? "span-bar-label-right" : "span-bar-label-left")">
                                        <span class="span-bar-label-detail">@GetResourceName(context.Span.Source): @context.GetDisplaySummary()</span>
                                        <span>@DurationFormatter.FormatDuration(context.Span.Duration)</span>
                                    </div>
                                </div>
                                <div class="tick" style="grid-column: 1;"></div>
                                <div class="tick" style="grid-column: 2;"></div>
                                <div class="tick" style="grid-column: 3;"></div>
                                <div class="tick" style="grid-column: 4;"></div>
                                <div class="tick" style="grid-column: 5;"></div>
                            </div>
                        </ChildContent>
                    </TemplateColumn>
                    <TemplateColumn Title="@ControlStringsLoc[nameof(ControlsStrings.DetailsColumnHeader)]" Class="no-ellipsis">
                        @{
                            var id = context.Span.SpanId;
                        }

                        <FluentButton
                            Id="@id"
                            Style="margin-left: 7px;"
                            Title="@ControlStringsLoc[nameof(ControlsStrings.ViewAction)]"
                            Appearance="Appearance.Lightweight" OnClick="@(() => OnShowPropertiesAsync(context, id))">@ControlStringsLoc[nameof(ControlsStrings.ViewAction)]</FluentButton>
                    </TemplateColumn>
                </FluentDataGrid>
            </Summary>
            <Details>
                <SpanDetails ViewModel="SelectedSpan" />
            </Details>
        </SummaryDetailsView>
    </div>
}
else
{
    <div class="empty-content">
        <FluentIcon Icon="Icons.Regular.Size24.GanttChart" /> &nbsp; @string.Format(Loc[nameof(Dashboard.Resources.TraceDetail.TraceDetailTraceNotFound)], TraceId)
    </div>
}
